<?php

/**
 * Description of RecordSet
 *
 * @author prometheus
 */
class XDS_RecordSet extends x10_Manager
{
	public $xName = 'XDS Adatsorok';

	/**
	 * @var XQL_Query
	 */
	protected $resultQuery;

	/**
	 * @var String
	 */
	protected $connectionAlias;

	/**
	 * @var XQL_Connection
	 */
	protected $connection;

	/**
	 * A leíró meghatározza az adatsorok mezőit és kulcsait az alábbi
	 * szerkezet alapján:
	 * <pre><code>{
	"connection": "default",
	"recordDescriptor": {
		"className": "XDS_RecordSet",
		"config": {
			"fields": [{
				"name": "field1",
				"text": "szöveg",
				"type": &lt;típus&gt;,
				"defaultValue": null,
				"dateFormat": "Y-m-d H:i:s",
				"sorting": {
					"mode": ["keyword", "fn"],
					"value": &lt;rendezési mód&gt;
				}
			}, {
				"name": "field2",
				"text": "szöveg",
				"type": &lt;típus&gt;,
				"defaultValue": null,
				"dateFormat": "Y-m-d H:i:s",
				"sorting": &lt;rendezési mód&gt;
			}],
			"PKFieldsNames": ["field1", "field2"]
		}
	}
}</code></pre>
	 * <ul>
	 * <li>&lt;típus&gt;: "auto", "string", "int", "float", "boolean", "date"</li>
	 * <li>&lt;rendezési mód&gt;: Egyszerűsített alakban (ld. field2) lehet ASC
	 * vagy DESC, részletes alakban mode="fn" esetén SQL függvény neve is lehet</li>
	 * </ul>
	 * @var Object
	 */
	protected $recordDescriptor;

	/**
	 * @var String
	 */
	protected $providerClassName = 'XDS_RecordProvider';

	/**
	 * @var Bool
	 */
	protected $opened = FALSE;
	
	/**
	 * A módosított rekordok sorszámai.
	 * @var Array
	 */
	protected $modified = array();
	
	/**
	 * A felvett rekordok sorszámai.
	 * @var Array
	 */
	protected $added = array();
	
	/**
	 * A törölt rekordok sorszámai.
	 * @var Array
	 */
	protected $removed = array();

	/**
	 * A rekordhalmaz tényleges rekordszámát jelenti.
	 * @var Int
	 */
	public $count = 0;

	/**
	 * Konstruktor.
	 * @param x10_Config $config
	 */
	public function __construct(x10_Config $config=NULL)
	{
		parent::__construct($config);
		$this->registerProvider(new x10_Provider_Descriptor('XDS_RecordProvider'));

		$this->recordDescriptor = $this->config->getConfigKey('recordDescriptor');
		$this->connectionAlias = $this->config->getConfigKey('connection');
		$this->connection = XQL_Manager::getConnection($this->connectionAlias);
		$pcn = $this->config->getConfigKey('providerClassName');
		if (!is_null($pcn)) $this->providerClassName = $pcn;
		$this->setupRecordDescriptor();
	}

	/**
	 * Megnyitja az adatsorkezelőt az induló feltöltéshez.
	 * Az induló feltöltést az XDS_DataSource osztály végzi, a megnyitás jelzi
	 * az adatsorkezelő számára, hogy a beszúrt rekordok nem "piszkosak" (azaz
	 * léteznek az adatforrásban).
	 */
	public function open()
	{
		$this->opened = TRUE;
	}

	public function isOpened()
	{
		return $this->opened;
	}

	/**
	 * Lezárja az adatsorkezelőt az induló feltöltés végén.
	 * A továbbiakban minden újonnan beszúrt mező "piszkos" jelzőt kap.
	 * @see open
	 */
	public function close()
	{
		$this->opened = FALSE;
	}

	public function getModifiedRecords()
	{
		$result = array();
		foreach ($this->modified as $idx)
		{
			$result[] = $this[$idx];
		}
		return $result;
	}

	public function getRemovedRecords()
	{
		$result = array();
		foreach ($this->removed as $idx)
		{
			$result[] = $this[$idx];
		}
		return $result;
	}

	public function getAddedRecords()
	{
		$result = array();
		foreach ($this->added as $idx)
		{
			$result[] = $this[$idx];
		}
		return $result;
	}

	/**
	 * Létrehoz egy új adatsor kiszolgálót a konfigurációban megadott
	 * {@link providerClassName kiszolgáló osztályból} példányosítva. Az új
	 * rekord megkapja a {@link recordDescriptor} beállításait tartalmazó
	 * objektumot, mint konfigurációt.
	 * @return XDS_RecordProvider
	 */
	public function createRecordProvider()
	{
		$result = new $this->providerClassName(new x10_Config($this->recordDescriptor));
		$result->setRecordSet($this);
		return $result;
	}

	public function getRecordDescriptor()
	{
		return $this->recordDescriptor;
	}

	public function append($value)
	{
		if (is_object($value) and ($newval instanceof XDS_RecordProvider))
		{
			if (!$this->opened) $value->appending();
			parent::append($value);
		}
		else if (is_array($value) and count($value) > 0)
		{
			$rec = $this->createRecordProvider();
			$rec->set($value);
			if (!$this->opened) $rec->appending();
			parent::append($rec);
		}
	}

	public function offsetSet($index, $newval)
	{
		if (is_null($index))
		{
			if (is_object($newval) and ($newval instanceof XDS_RecordProvider))
			{
				if (!$this->opened) $newval->appending();
				$newval->setRecordSet($this);
				$newval->setRecordIdx(count($this));
				parent::offsetSet($index, $newval);
			}
			else if (is_array($newval) and count($newval) > 0)
			{
				$rec = $this->createRecordProvider();
				$rec->set($newval);
				$rec->setRecordIdx(count($this));
				if (!$this->opened) $rec->appending();
				parent::offsetSet($index, $rec);
			}
		}
		else
		{
			if (is_object($newval) and ($newval instanceof XDS_RecordProvider))
			{
				if (isset($this[$index])) $this[$index]->changing($newval);
				parent::offsetSet($index, $newval);
			}
			else if (is_array($newval) and count($newval) > 0)
			{
				$rec = $this->createRecordProvider();
				$rec->set($newval);
				if (isset($this[$index])) $this[$index]->changing($rec);
				parent::offsetSet($index, $rec);
			}
		}
	}

	public function offsetUnset($index)
	{
		if (isset($this[$index])) $this[$index]->removing();
	}

	/**
	 * Az elsődleges kulcs alapján módosít egy meglévő rekordot.
	 *
	 * @param mixed $pk A mindenkori PK-nak megfelelően a PK értéke lehet:<ul>
	 * <li>egyszerű érték: ha a PK csak egyetlen mezőt tartalmaz</li>
	 * <li>egyszerű felsorolás: tömbként, a PK mezőinek sorrendjében</li>
	 * <li>asszociatív tömb: a PK mezőneveihez párosított értékekként</li>
	 * <li>objektum: lásd asszociatív tömb</li>
	 * </ul>
	 * @param XDS_RecordProvider $provider
	 * @return Bool
	 */
	public function updateByPK($pk, XDS_RecordProvider $provider)
	{
		$rowId = $this->getRowIdByPK($pk);
		if ($rowId !== FALSE) $this[$rowId] = $provider;
		return (bool)$rowId;
	}

	/**
	 * Törlésre jelöli az adott rekordot az elsődleges kulcs alapján.
	 *
	 * @param mixed $pk A mindenkori PK-nak megfelelően a PK értéke lehet:<ul>
	 * <li>egyszerű érték: ha a PK csak egyetlen mezőt tartalmaz</li>
	 * <li>egyszerű felsorolás: tömbként, a PK mezőinek sorrendjében</li>
	 * <li>asszociatív tömb: a PK mezőneveihez párosított értékekként</li>
	 * <li>objektum: lásd asszociatív tömb</li>
	 * </ul>
	 * @param XDS_RecordProvider $provider
	 * @return Bool
	 */
	public function removeByPK($pk, XDS_RecordProvider $provider)
	{
		$rowId = $this->getRowIdByPK($pk);
		if ($rowId !== FALSE) unset($this[$rowId]);
		return (bool)$rowId;
	}

	public function getRowIdByPK($pk)
	{
		$pk = $this->normalizePKData($pk);
		$result = FALSE;
		foreach ((array)$this as $rowId=>$record)
		{
			$found = TRUE;
			foreach ($pk as $fieldName=>$value)
			{
				$rowData = $record->get();
				if (!isset($rowData->$fieldName) or $rowData->$fieldName != $value)
				{
					$found = FALSE;
				}
			}
			
			if ($found)
			{
				$result = $rowId;
				break;
			}
		}
		return $result;
	}

	/**
	 * Csak a szolgáltató hívhatja!
	 * Regisztrálja egy adatsor változását.
	 *
	 * @param String $event modified|added|removed
	 * @param Int $idx
	 */
	public function registerChange($event, $idx)
	{
		$this->{$event}[] = $idx;
	}

	protected function normalizePKData($pk)
	{
		$result = array();
		if (is_array($pk))
		{
			$cnt = 0;
			foreach ($pk as $k=>$v)
			{
				if (in_array($k, $this->recordDescriptor->PKFieldsNames))
				{
					$result[$k] = $v;
				}
				else
				{
					$result[$this->recordDescriptor->PK[$cnt]->name] = $v;
					$cnt++;
				}
			}
		}
		else if (is_object($pk))
		{
			foreach ($pk as $k=>$v) $result[$k] = $v;
		}
		else
		{
			$result[$this->recordDescriptor->PK[0]->name] = $pk;
		}

		return $result;
	}

	protected function setupRecordDescriptor()
	{
		$this->recordDescriptor->PK = array();
		if (isset($this->recordDescriptor->PKFieldsNames) and is_array($this->recordDescriptor->PKFieldsNames))
		{
			foreach ($this->recordDescriptor->PKFieldsNames as $fieldName)
			{
				$srcField = NULL;
				foreach ($this->recordDescriptor->fields as $fieldData)
				{
					if ($fieldData->name == $fieldName)
					{
						$srcField = $fieldData;
						break;
					}
				}
				if (!is_null($srcField)) $this->recordDescriptor->PK[] = $srcField;
			}
		}
	}
}

?>